﻿using System.Linq;
using System.Web.Mvc.Html;
using System.Collections.Generic;
using System.Text;

namespace System.Web.Mvc
{
    /// <summary>
    /// Well-formed XHTML Calendar for ASP.NET MVC
    /// </summary>
    public class Calendar
    {
        /// <summary>
        /// Gets the name of current calendar, it's diaplayed as the "id" of the HTML element "table"
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Gets the selected date of current calendar
        /// </summary>
        public DateTime SelectedDate { get; private set; }

        /// <summary>
        /// Gets the date array which to generate the links
        /// </summary>
        public IEnumerable<DateTime> RawDates { get; private set; }

        /// <summary>
        /// Gets the controller name that to generate the daily links
        /// </summary>
        public string ControllerName { get; private set; }

        /// <summary>
        /// Gets the action name that to generate the daily links
        /// </summary>
        public string ActionName { get; private set; }

        private readonly HtmlHelper _helper;

        public bool WithNavigation { get; set; }

        /// <summary>
        /// default calendar
        /// </summary>
        public Calendar()
        {
            SelectedDate = DateTime.Now;
            Name = "MvcCalendar";
        }

        /// <summary>
        /// Create a calendar use the specified name
        /// </summary>
        /// <param name="name">the name of calendar</param>
        public Calendar(string name)
            : this()
        {
            Name = name;
        }

        /// <summary>
        /// Create a calendar use the specified name and selected date
        /// </summary>
        /// <param name="name">the name of calendar</param>
        /// <param name="selectedDate">selected date</param>
        public Calendar(string name, DateTime selectedDate)
            : this(name)
        {
            SelectedDate = selectedDate;
        }

        /// <summary>
        /// Create a calendar use data that generate links
        /// </summary>
        /// <param name="name">the name of calendar</param>
        /// <param name="selectedDate">selected date</param>
        /// <param name="rawDates">the date array</param>
        /// <param name="helper">HtmlHelper object</param>
        /// <param name="controllerName">controller name</param>
        /// <param name="actionName">action name</param>
        public Calendar(string name, DateTime selectedDate, IEnumerable<DateTime> rawDates, HtmlHelper helper, string controllerName, string actionName)
            : this(name)
        {
            SelectedDate = selectedDate;
            RawDates = rawDates;
            _helper = helper;
            ControllerName = controllerName;
            ActionName = actionName;
            WithNavigation = true;
        }

        public Calendar(string name, DateTime selectedDate, IEnumerable<DateTime> rawDates, string controllerName, string actionName)
            : this(name)
        {
            SelectedDate = selectedDate;
            RawDates = rawDates;
            ControllerName = controllerName;
            ActionName = actionName;
        }

        private string GenerateDateSelection()
        {

            var now = DateTime.Now;
            var date = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);

            StringBuilder sql = new StringBuilder();
            sql.AppendLine("<div class=\"eventCalendar-header\"> ");
            sql.AppendLine("        <a href=\"#\" title=\"Prev\" class=\"prev\"><span class=\"eventCalendar-prev\">Prev</span></a> ");
            sql.AppendLine("        <a href=\"#\" title=\"Next\" class=\"next\"><span class=\"eventCalendar-next\">Next</span></a> ");
            sql.AppendLine("        <div class=\"eventCalendar-title\"> ");
            sql.AppendLine("            <select class=\"eventCalendar-month\" style=\"width: 75px;\"> ");
            for (int i = 1; i <= 12; i++)
            {
                sql.AppendFormat(" <option{0}value=\"{1}\">{2}</option>", i == SelectedDate.Month ? " selected=\"selected\" " : " ", i.ToString(), i.GetMonthName());
            }
            sql.AppendLine("            </select> ");
            int year = SelectedDate.Year - 5;
            sql.AppendLine("            <select class=\"eventCalendar-year\" style=\"width: 75px;\"> ");
            for (int i = year; i < (year + 10); i++)
            {
                sql.AppendFormat(" <option{0}value=\"{1}\">{2}</option>", i == SelectedDate.Year ? " selected=\"selected\" " : " ", i.ToString(), i.ToString());
            }
            sql.AppendLine("            </select> ");
            sql.AppendLine("        </div> ");
            sql.AppendLine("    </div> ");


            return sql.ToString();
        }

        private string GenerateHeader()
        {
            var prevLink = String.Empty;
            var nextLink = String.Empty;

            var header = new StringBuilder();
            header.AppendFormat("<table id=\"{0}\" class=\"tbCalendar\">", Name);

            #region
            //if (_helper != null && RawDates != null)
            //{
            //    bool hasPrev = true;
            //    bool hasNext = true;

            //    foreach (var date in RawDates)
            //    {
            //        if (date.Year == SelectedDate.Year && date.Month < SelectedDate.Month) hasPrev = true;
            //        if (date.Year == SelectedDate.Year && date.Month > SelectedDate.Month) hasNext = true;
            //    }

            //    if (hasPrev) prevLink = _helper.ActionLink("<<", ActionName, new { date = string.Format("{0}-{1}", SelectedDate.AddMonths(-1).Year, SelectedDate.AddMonths(-1).Month) }).ToString();
            //    if (hasNext) nextLink = _helper.ActionLink(">>", ActionName, new { date = string.Format("{0}-{1}", SelectedDate.AddMonths(1).Year, SelectedDate.AddMonths(1).Month) }).ToString();
            //}

            //header.AppendFormat("<div class=\"ui-datepicker-header ui-widget-header ui-helper-clearfix ui-corner-all\">");
            //header.AppendFormat("</div>");
            //header.AppendFormat("<caption>{0} {1} {2}</caption>", prevLink, SelectedDate.ToString("y"), nextLink);
            #endregion

            header.Append("<tr>");

            //header.Append("<th scope=\"col\" abbr=\"Sunday\" title=\"Sunday\">Sun</th>");
            //header.Append("<th scope=\"col\" abbr=\"Monday\" title=\"Monday\">Mon</th>");
            //header.Append("<th scope=\"col\" abbr=\"Tuesday\" title=\"Tuesday\">Tue</th>");
            //header.Append("<th scope=\"col\" abbr=\"Wednesday\" title=\"Wednesday\">Wed</th>");
            //header.Append("<th scope=\"col\" abbr=\"Thursday\" title=\"Thursday\">Thu</th>");
            //header.Append("<th scope=\"col\" abbr=\"Friday\" title=\"Friday\">Fri</th>");
            //header.Append("<th scope=\"col\" abbr=\"Saturday\" title=\"Saturday\">Sat</th>");

            header.Append("<th scope=\"col\" abbr=\"Sunday\" title=\"Sunday\">Su</th>");
            header.Append("<th scope=\"col\" abbr=\"Monday\" title=\"Monday\">Mo</th>");
            header.Append("<th scope=\"col\" abbr=\"Tuesday\" title=\"Tuesday\">Tu</th>");
            header.Append("<th scope=\"col\" abbr=\"Wednesday\" title=\"Wednesday\">We</th>");
            header.Append("<th scope=\"col\" abbr=\"Thursday\" title=\"Thursday\">Th</th>");
            header.Append("<th scope=\"col\" abbr=\"Friday\" title=\"Friday\">Fr</th>");
            header.Append("<th scope=\"col\" abbr=\"Saturday\" title=\"Saturday\">Sa</th>");

            //header.Append("<th scope=\"col\" abbr=\"星期日\" title=\"星期日\">日</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期一\" title=\"星期一\">一</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期二\" title=\"星期二\">二</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期三\" title=\"星期三\">三</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期四\" title=\"星期四\">四</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期五\" title=\"星期五\">五</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期六\" title=\"星期六\">六</th>");

            header.Append("</tr>");

            return header.ToString();
        }

        private string GenerateFooter()
        {
            return "</table>";
        }

        private string GenerateDays()
        {
            var now = DateTime.Now;
            var date = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
            var days = DateTime.DaysInMonth(SelectedDate.Year, SelectedDate.Month);
            var weekDayOfFirstDay = (int)date.DayOfWeek;

            var daysXhtml = new StringBuilder();

            int day = 1;//first day of the date's month
            int weekDay = 0;//0 - 6

            if (weekDayOfFirstDay > 0)
            {
                daysXhtml.Append("<tr>");
                while (weekDay < weekDayOfFirstDay)
                {
                    daysXhtml.Append("<td class=\"none\">&nbsp;</td>");
                    weekDay++;
                }
            }

            while (day <= days)
            {
                if (weekDay == 0) daysXhtml.Append("<tr>");

                if (date.Year == now.Year && date.Month == now.Month && day == now.Day)
                    daysXhtml.AppendFormat("<td class=\"today\">{0}</td>", day);
                else
                    daysXhtml.AppendFormat("<td>{0}</td>", day);

                if (weekDay == 6)
                {
                    daysXhtml.Append("</tr>");
                    weekDay = -1;
                }

                day++;
                weekDay++;
            }

            if (weekDay > 0)
            {
                while (weekDay < 7)
                {
                    daysXhtml.Append("<td class=\"none\">&nbsp;</td>");
                    weekDay++;
                }
                daysXhtml.Append("</tr>");
            }

            return daysXhtml.ToString();
        }

        private string GenerateDays(IEnumerable<DateTime> rawDates)
        {
            var now = DateTime.Now;
            var date = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
            var days = DateTime.DaysInMonth(SelectedDate.Year, SelectedDate.Month);
            var weekDayOfFirstDay = (int)date.DayOfWeek;

            var daysXhtml = new StringBuilder();

            int day = 1;//first day of the date's month
            int weekDay = 0;//0 - 6

            if (weekDayOfFirstDay > 0)
            {
                daysXhtml.Append("<tr>");
                while (weekDay < weekDayOfFirstDay)
                {
                    daysXhtml.Append("<td class=\"none\">&nbsp;</td>");
                    weekDay++;
                }
            }

            while (day <= days)
            {
                if (weekDay == 0) daysXhtml.Append("<tr>");

                var caldate = new DateTime(date.Year, date.Month, day);
                int count = (from d in rawDates
                             where d.Date == caldate.Date
                             select d).Count();
                string dayString = day.ToString();

                DateTime dateStart = Convert.ToDateTime(String.Format("{0}/{1}/{2}", caldate.Month, caldate.Day, caldate.Year));

                if (count > 0)
                {
                    //dayString = _helper.ActionLink(day.ToString(), ActionName, ControllerName, new { datestart = dateStart.ToString("MM/dd/yyyy") }, null).ToString();
                    dayString = String.Format("<a href=\"/{0}/{1}?datestart={2}&c=1\">{3}</a>", ControllerName, ActionName, dateStart.ToString("MM/dd/yyyy"), day.ToString());
                }
                if (date.Year == now.Year && date.Month == now.Month && day == now.Day)
                    daysXhtml.AppendFormat("<td class=\"today\">{0}</td>", dayString);
                else
                    daysXhtml.AppendFormat("<td>{0}</td>", dayString);

                if (weekDay == 6)
                {
                    daysXhtml.Append("</tr>");
                    weekDay = -1;
                }

                day++;
                weekDay++;
            }

            if (weekDay > 0)
            {
                while (weekDay < 7)
                {
                    daysXhtml.Append("<td class=\"none\">&nbsp;</td>");
                    weekDay++;
                }
                daysXhtml.Append("</tr>");
            }

            return daysXhtml.ToString();
        }

        /// <summary>
        /// Returns the built calendar
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            var calendar = new StringBuilder();

            if (WithNavigation)
            {
                calendar.AppendLine(GenerateDateSelection());
            }

            calendar.AppendLine("<div id=\"eventCalendar-table\">");

            if (RawDates != null)
            {
                //Build a calendar with links
                calendar.AppendFormat("{0}{1}{2}",
                    GenerateHeader(),
                    GenerateDays(RawDates),
                    GenerateFooter()
                    );
            }
            else
            {
                //build an empty calendar for current month
                calendar.AppendFormat("{0}{1}{2}",
                    GenerateHeader(),
                    GenerateDays(),
                    GenerateFooter()
                    );
            }

            calendar.AppendLine("</div>");

            return calendar.ToString();
        }
    }

}